package com.polaris.live.common.manager

import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.polaris.live.common.util.LazyUtils
import com.polaris.live.common.util.LogExt
import com.polaris.live.common.util.resettableLazy
import com.polaris.live.common.util.resettableManager
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.math.max

/**
 * 所有公有调度都应该写到这里来
 *
 * @author Created by lucas on 2023/11/1 14:11
 */
object CoroutineScopeManager {

    /**
     * 主线程调度器
     */
    val mainDispatcher = Dispatchers.Main

    /**
     * io调度器
     */
    val defaultDispatcher = Dispatchers.Default

    /**
     * io调度器
     */
    val ioDispatcher = Dispatchers.IO

    private val resettableManager = resettableManager()

    private val handler = CoroutineExceptionHandler { _, e ->
        LogExt.logE("ErrorHandler", "CoroutineScopeManager", e)

        FirebaseCrashlytics.getInstance().recordException(e)
    }

    /**
     * 默认的协程作用域
     */
    val defaultScope by resettableLazy(resettableManager) {
        CoroutineScope(defaultDispatcher + SupervisorJob() + handler)
    }

    /**
     * io的协程作用域
     */
    val ioScope by resettableLazy(resettableManager) {
        CoroutineScope(ioDispatcher + SupervisorJob() + handler)
    }

    /**
     * main的协程作用域，不会抛出异常
     */
    val mainScope by resettableLazy(resettableManager) {
        CoroutineScope(mainDispatcher + SupervisorJob() + handler)
    }

    /**
     * 重置所有的调度
     */
    fun reset() {
        val defaultScope = defaultScope
        val ioScope = ioScope
        resettableManager.reset()
        defaultScope.cancel()
        ioScope.cancel()
    }
}

/**
 * 延迟执行
 *
 * @param timeInMillis 总延迟时间
 * @param interval 间隔时间
 * @param onTick 每次间隔执行的回调
 * @param onFinish 倒计时结束后的回调，不管如何都会执行，为0代表正常结束
 */
fun CoroutineScope.startCountdown(
    timeInMillis: Long,
    interval: Long,
    onTick: suspend (Long) -> Unit,
    onFinish: (CoroutineScope.(Long) -> Unit)? = null
): Job {
    return launch {
        withCountdown(timeInMillis, interval, onTick) {
            onFinish?.invoke(this, it)
        }
    }
}

/**
 * 延迟执行
 *
 * @param timeInMillis 总延迟时间
 * @param interval 间隔时间
 * @param onTick 每次间隔执行的回调
 * @param onFinish 倒计时结束后的回调，不管如何都会执行，为0代表正常结束
 */
suspend fun withCountdown(
    timeInMillis: Long,
    interval: Long,
    onTick: suspend (Long) -> Unit,
    onFinish: (suspend (Long) -> Unit)? = null,
) = coroutineScope {
    var timeRemaining = timeInMillis

    try {
        while (isActive && timeRemaining >= 0) {
            val startTime = System.currentTimeMillis()
            onTick(timeRemaining)

            // 计算onTick执行的时间
            val tickDuration = System.currentTimeMillis() - startTime

            // 计算下一个间隔前的延迟时间
            val delayTime = interval - tickDuration
            if (delayTime > 0) {
                delay(delayTime)
            }
            timeRemaining -= interval
        }
    } finally {
        onFinish?.invoke(max(timeRemaining, 0))
    }
}

/**
 * 失败后立即重试一次
 *
 * @param block 代码块
 */
suspend fun <T> withRetryNow(block: suspend () -> T): T {
    return withRetry(2, { 0 }, block)
}

/**
 * 重试，采用退避策略
 *
 * @param times 重试次数
 * @param initDelay 初始延迟
 * @param baseDelay 基础延迟
 * @param maxDelay 最大延迟
 * @param exponent 指数
 * @param block 代码块
 */
suspend fun <T> withRetry(
    times: Int,
    initDelay: Long = 1000,
    baseDelay: Long = 1000,
    maxDelay: Long = 15 * 1000,
    exponent: Double? = null,
    block: suspend () -> T,
): T {
    return withRetry(
        times,
        { LazyUtils.exponentialBackoff(it, initDelay, baseDelay, maxDelay, exponent) },
        block
    )
}

/**
 * 重试
 *
 * @param times 重试次数
 * @param delay 延迟时间
 * @param block 代码块
 */
suspend fun <T> withRetry(
    times: Int,
    delay: Long,
    block: suspend () -> T,
): T {
    return withRetry(times, { delay }, block)
}

/**
 * 重试
 *
 * @param times 重试次数
 * @param delayBlock 延迟时间计算
 * @param block 代码块
 */
suspend fun <T> withRetry(
    times: Int,
    delayBlock: (Int) -> Long,
    block: suspend () -> T,
): T = coroutineScope {
    if (times <= 0) {
        throw IllegalArgumentException("times must be greater than 0")
    }

    var lastThrowable: Throwable? = null
    var currentAttempt = 0
    while (isActive && currentAttempt < times) {
        try {
            return@coroutineScope block()
        } catch (t: Throwable) {
            lastThrowable = t

            if (currentAttempt++ < times) {
                val delay = delayBlock(currentAttempt)
                if (delay > 0) {
                    delay(delay)
                }
            } else {
                throw t
            }
        }
    }

    if (lastThrowable != null) {
        throw lastThrowable
    } else {
        throw IllegalStateException("unreachable")
    }
}

/**
 * 异步转协程同步
 */
suspend fun <T> withDeferred(block: suspend CompletableDeferred<T>.() -> Unit): T {
    return CompletableDeferred<T>().apply { block() }.await()
}